package com.changgou.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.changgou.search.pojo.SkuInfo;
import com.changgou.search.service.SearchService;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

/***
 * 1.构建请求
 * 2.执行操作
 * 3.获取结果
 */

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    /***
     * 搜索方法
     * @param searchMap
     * @return
     */
    @Override
    public Map search(Map<String, String> searchMap) {
        //1.构建请求
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();

        //构建一个bool查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        if (searchMap != null) {

            //按照关键词查询
            if (!StringUtils.isEmpty(searchMap.get("keywords"))) {
                MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", searchMap.get("keywords")).operator(Operator.AND);
                boolQueryBuilder.must(matchQueryBuilder);
            }

            //按照品牌查询
            if (!StringUtils.isEmpty(searchMap.get("brand"))) {
                //brandName 是keyword类型 用term查询
                TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("brandName", searchMap.get("brand"));
                //term range 要不就满足,要不就不满足,应该放在filter中
                boolQueryBuilder.filter(termQueryBuilder);
            }

            //按照规格查询
            for (String key : searchMap.keySet()) {
                //把controller 的"%2B" 再替换成"+"
                String value = searchMap.get(key);
                value = value.replace("%2B", "+");

                if (key.startsWith("spec_")) {
                    TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("specMap." + key.substring(5) + ".keyword", value);
                    boolQueryBuilder.filter(termQueryBuilder);
                }
            }

            //按照价格查询 price=0-500 2000-3000 3000+
            if (!StringUtils.isEmpty(searchMap.get("price"))) {
                //切割值,获得一个数组
                String[] prices = searchMap.get("price").split("-");

                //双区间
                if (prices.length == 2) {
                    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price").gte(prices[0]).lte(prices[1]);
                    boolQueryBuilder.filter(rangeQueryBuilder);
                } else {
                    //单区间
                    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("price").gte(prices[0]);
                    boolQueryBuilder.filter(rangeQueryBuilder);
                }
            }
        }

        //设置查询条件
        nativeSearchQueryBuilder.withQuery(boolQueryBuilder);

        //开启分页查询
        String pageNum = searchMap.get("pageNum");
        String pageSize = searchMap.get("pageSize");
        //前端没有传来数据的话,默认值
        if (StringUtils.isEmpty(pageNum)) {
            pageNum = "1";
        }
        if (StringUtils.isEmpty(pageSize)) {
            pageSize = "30";
        }
        //PageRequest 页码是从0开始
        PageRequest pageRequest = PageRequest.of(Integer.parseInt(pageNum) - 1, Integer.parseInt(pageSize));
        nativeSearchQueryBuilder.withPageable(pageRequest);

        //按照相关字段排序查询
        if (!StringUtils.isEmpty(searchMap.get("sortField")) && !StringUtils.isEmpty(searchMap.get("sortRule"))) {
            //升序
            if (searchMap.get("sortRule").equals("ASC")) {
                FieldSortBuilder sortField = SortBuilders.fieldSort(searchMap.get("sortField")).order(SortOrder.ASC);
                nativeSearchQueryBuilder.withSort(sortField);
            } else {
                //降序
                FieldSortBuilder sortField = SortBuilders.fieldSort(searchMap.get("sortField")).order(SortOrder.DESC);
                nativeSearchQueryBuilder.withSort(sortField);
            }
        }

        //设置高亮
        HighlightBuilder.Field field = new HighlightBuilder.Field("name");
        //前缀
        field.preTags("<font color='red'>");
        //后缀
        field.postTags("</font>");
        nativeSearchQueryBuilder.withHighlightFields(field);

        //添加品牌聚合
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("skuBrand").field("brandName");
        nativeSearchQueryBuilder.addAggregation(termsAggregationBuilder);

        //按照规格聚合查询
        TermsAggregationBuilder termsAggregationBuilder1 = AggregationBuilders.terms("skuSpec").field("spec.keyword");
        nativeSearchQueryBuilder.addAggregation(termsAggregationBuilder1);

        /***
         * 三板斧 ---> 大宝剑
         * SearchQuery query 搜索条件
         * Class<T> clazz 结果类型
         * SearchResultMapper mapper 封装结果
         */

        //2.执行操作
        AggregatedPage<SkuInfo> skuInfos = elasticsearchTemplate.queryForPage(nativeSearchQueryBuilder.build(), SkuInfo.class, new SearchResultMapper() {
            //3.获取结果
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
                List<T> list = new ArrayList<>();

                SearchHits hits = searchResponse.getHits();
                SearchHit[] realHits = hits.getHits();
                for (SearchHit realHit : realHits) {
                    //获取每一个结果的source
                    String sourceAsString = realHit.getSourceAsString();
                    //转化为skuInfo对象
                    SkuInfo skuInfo = JSON.parseObject(sourceAsString, SkuInfo.class);

                    //获取高亮字段,替换原来的name属性
                    Map<String, HighlightField> highlightFields = realHit.getHighlightFields();
                    //获取特定的高亮字段
                    HighlightField highlightField = highlightFields.get("name");
                    //避免不输入关键字报错
                    if (highlightField != null) {
                        //拿出数据
                        Text[] fragments = highlightField.getFragments();
                        StringBuffer stringBuffer = new StringBuffer();
                        //拼接数据为string
                        for (Text fragment : fragments) {
                            stringBuffer.append(fragment);
                        }
                        String highLightName = stringBuffer.toString();
                        //设置到name当中
                        skuInfo.setName(highLightName);
                    }

                    //放到list
                    list.add((T) skuInfo);
                }
                return new AggregatedPageImpl<T>(list, pageable, hits.getTotalHits(), searchResponse.getAggregations());
            }
        });

        //返回数据map
        Map<String, Object> resultMap = new HashMap<>();
        //数据List
        resultMap.put("rows", skuInfos.getContent());
        //总数
        resultMap.put("total", skuInfos.getTotalElements());
        //总页数
        resultMap.put("totalPages", skuInfos.getTotalPages());

        //获取品牌聚合
        /*List<String> brandList = new ArrayList<>();*/
        //拿到所有的聚合
        /*Aggregations aggregations = skuInfos.getAggregations();*/
        //只拿我们想要的聚合
        /*Terms skuBrand = aggregations.get("skuBrand");*/
        /*List<? extends Terms.Bucket> buckets = skuBrand.getBuckets();
        //遍历buckets
        for (Terms.Bucket bucket : buckets) {
            String keyAsString = bucket.getKeyAsString();
            brandList.add(keyAsString);
        }*/

        //java8 stream流特性
        List<String> brandList = ((Terms) skuInfos.getAggregations().get("skuBrand"))
                .getBuckets().stream().map((bucket) -> ((Terms.Bucket) bucket).getKeyAsString())
                .collect(Collectors.toList());

        resultMap.put("brandList", brandList);

        //按照规格进行聚合
        List<String> specList = ((Terms) skuInfos.getAggregations().get("skuSpec"))
                .getBuckets().stream().map((bucket) -> ((Terms.Bucket) bucket).getKeyAsString())
                .collect(Collectors.toList());
        resultMap.put("specList", formartSpec(specList));

        //当前页
        resultMap.put("pageNum", pageNum);

        return resultMap;
    }

            /*"{'颜色': '红色'}",
            "{'颜色': '红色', '版本': '4GB+64GB'}",
            "{'颜色': '红色', '版本': '6GB+64GB'}",
            "{'颜色': '红色', '版本': '6GB+128GB'}",
            "{'颜色': '红色', '版本': '4GB+128GB'}",
            "{'颜色': '红色', '版本': '4GB+32GB'}",
            "{'颜色': '红色', '版本': '3GB+32GB'}",
            "{'颜色': '红色', '版本': '8GB+128GB'}",
            "{'颜色': '红色', '版本': '20GB+128GB'}",
            "{'颜色': '红色', '版本': '64G'}"*/

    /*{
        颜色:[蓝色,黑色,金色],
        版本:[6BG+128GB,4GB+64GB]
    }*/

    public Map<String, Set<String>> formartSpec(List<String> specList) {
        //创建结果map
        Map<String, Set<String>> resultMap = new HashMap<>();

        //循环原始的specList
        for (String spec : specList) {
            Map<String, String> map = JSON.parseObject(spec, Map.class);

            for (String key : map.keySet()) {
                String value = map.get(key);
                //往结果里塞数据
                Set<String> resultSet = resultMap.get(key);
                if (resultSet == null) {
                    resultSet = new HashSet<>();
                }
                resultSet.add(value);
                //set放入返回的map中
                resultMap.put(key, resultSet);
            }
        }
        return resultMap;
    }
}
